<?php
declare (strict_types=1);

namespace app\common\command;


use think\console\Command;
use think\console\Input;
use think\console\input\Option;
use think\console\Output;
use think\facade\Db;
use think\helper\Str;


class Curd extends Command
{
    /**
     * 项目根路径
     * @var string
     */
    protected $appPath;

    /**
     * 模板文件地址
     * @var string
     */
    protected $tplPath;

    /**
     * 初始化
     */
    public function __construct()
    {
        parent::__construct();
        !defined('DS') && define('DS', DIRECTORY_SEPARATOR);
        $this->appPath = app()->getAppPath();
        $this->tplPath = $this->appPath . 'common' . DS . 'command' . DS . 'curd' . DS;
    }

    /**
     * @notes 命令配置
     * @author mjf
     * @date 2022/5/19 12:03
     */
    protected function configure()
    {
        $this->setName('curd')
            ->addOption('table', 't', Option::VALUE_OPTIONAL, '数据库表名')
            ->addOption('module', 'm', Option::VALUE_OPTIONAL, '模块名')
            ->setDescription('创建curd相关文件');
    }

    /**
     * @notes 执行逻辑
     * @param Input $input
     * @param Output $output
     * @return int|void|null
     * @author mjf
     * @date 2022/5/19 12:04
     */
    protected function execute(Input $input, Output $output)
    {
        try {
            $tableName = $input->getOption('table');
            if (!$tableName) {
                throw new \Exception("请输入 -t 表名");
            }

            $moduleName = $input->getOption('module');
            if (!$moduleName) {
                $moduleName = 'index';
            }

            // 创建文件前校验是否已存在
            $this->makeFileBefore($tableName, $moduleName);

            // 创建文件
            $this->makeController($tableName, $moduleName);
            $this->makeModel($tableName, $moduleName);
            $this->makeService($tableName, $moduleName);
            $this->makeValidate($tableName, $moduleName);

            $output->writeln('create success');

        } catch (\Exception $e) {
            $output->error($e->getMessage());
            exit;
        }
    }

    /**
     * @notes 创建控制器文件
     * @param $tableName
     * @param $moduleName
     * @author mjf
     * @date 2022/5/19 11:44
     */
    protected function makeController($tableName, $moduleName)
    {
        // 获取控制器模板文件
        $controllerTpl = $this->getTemplatePath('controller');
        // 替换模板中的变量
        $controllerTpl = str_replace(
            ['<module>', '<controller>', '<service>', '<validate>'],
            [strtolower($moduleName), Str::studly($tableName), Str::studly($tableName), Str::studly($tableName)],
            file_get_contents($controllerTpl)
        );

        // 控制器文件信息
        $fileData = $this->getFileData('controller', $tableName, $moduleName);
        // 创建控制器文件
        file_put_contents($fileData['file_path'], $controllerTpl);

        // 检测对应模块base控制器是否存在
        $moduleNameBaseController = $fileData['dir'] . 'Controller.php';
        if (!file_exists($moduleNameBaseController)) {
            // 获取基础控制器模板
            $baseControllerTpl = $this->getTemplatePath('base_controller');
            // 替换模板变量
            $baseControllerTpl = str_replace('<module>', strtolower($moduleName), file_get_contents($baseControllerTpl));
            file_put_contents($fileData['dir'] . 'Controller.php', $baseControllerTpl);
        }
    }

    /**
     * @notes 创建模型文件
     * @param $tableName
     * @param $moduleName
     * @return false|int
     * @author mjf
     * @date 2022/5/19 11:43
     */
    public function makeModel($tableName, $moduleName)
    {
        // 获取模板文件
        $modelTpl = $this->getTemplatePath('model');
        // 模型文件信息
        $fileData = $this->getFileData('model', $tableName, $moduleName);
        //替换模板变量
        $modelTpl = str_replace(
            ['<module>', '<model>', '<pk>', '<tableName>'],
            [strtolower($moduleName), Str::studly($tableName), $this->getTablePk($tableName), $tableName],
            file_get_contents($modelTpl)
        );
        return file_put_contents($fileData['file_path'], $modelTpl);
    }

    /**
     * @notes 创建验证器文件
     * @param $tableName
     * @param $moduleName
     * @return false|int
     * @author mjf
     * @date 2022/5/19 11:43
     */
    public function makeValidate($tableName, $moduleName)
    {
        // 获取模板文件
        $validateTpl = $this->getTemplatePath('validate');
        // 验证器文件信息
        $fileData = $this->getFileData('validate', $tableName, $moduleName);
        // 替换模板变量
        $validateTpl = str_replace(
            ['<module>', '<validate>'],
            [strtolower($moduleName), Str::studly($tableName)],
            file_get_contents($validateTpl)
        );
        return file_put_contents($fileData['file_path'], $validateTpl);
    }

    /**
     * @notes 创建逻辑层文件
     * @param $tableName
     * @param $moduleName
     * @return false|int
     * @author mjf
     * @date 2022/5/19 11:43
     */
    public function makeService($tableName, $moduleName)
    {
        // 获取模板文件
        $serviceTpl = $this->getTemplatePath('service');
        // 逻辑文件信息
        $fileData = $this->getFileData('service', $tableName, $moduleName);
        // 替换模板变量
        $serviceTpl = str_replace(
            ['<module>', '<service>', '<model>', '<pk>'],
            [strtolower($moduleName), Str::studly($tableName), Str::studly($tableName), $this->getTablePk($tableName)],
            file_get_contents($serviceTpl)
        );
        return file_put_contents($fileData['file_path'], $serviceTpl);
    }

    /**
     * @notes 获取模板对应地址
     * @param $name
     * @return string
     * @author mjf
     * @date 2022/5/19 11:43
     */
    protected function getTemplatePath($name)
    {
        $tpl = [
            'base_controller' => 'BaseController.tpl',
            'controller' => 'Controller.tpl',
            'model' => 'Model.tpl',
            'service' => 'Service.tpl',
            'validate' => 'Validate.tpl',
        ];
        $tplName = $tpl[$name] ?? '';
        return $this->tplPath . $tplName;
    }

    /**
     * @notes 获取数据表主键
     * @param $tableName
     * @return mixed|string
     * @author mjf
     * @date 2022/5/19 11:42
     */
    protected function getTablePk($tableName)
    {
        $prefix = config('database.connections.mysql.prefix');
        $column = Db::query('SHOW FULL COLUMNS FROM `' . $prefix . $tableName . '`');
        $pk = 'id';
        foreach ($column as $vo) {
            if ($vo['Key'] == 'PRI') {
                $pk = $vo['Field'];
                break;
            }
        }
        return $pk;
    }

    /**
     * @notes 校验讲创建的文件夹是否已存在
     * @param $tableName
     * @param $moduleName
     * @throws \Exception
     * @author mjf
     * @date 2022/5/19 11:38
     */
    protected function makeFileBefore($tableName, $moduleName)
    {
        // 需校验的文件类型
        $fileType = ['controller', 'model', 'validate', 'service'];
        foreach ($fileType as $type) {
            $fileData = $this->getFileData($type, $tableName, $moduleName);
            // 如不存在文件夹则创建
            !is_dir($fileData['dir']) && mkdir($fileData['dir'], 0755, true);
            // 校验将生成的文件是否已存在
            if (file_exists($fileData['file_path'])) {
                throw new \Exception($fileData['file_path'] . '已存在');
            }
        }
    }

    /**
     * @notes 获取将生成文件的信息
     * @param $type
     * @param $tableName
     * @param $moduleName
     * @return array
     * @author mjf
     * @date 2022/5/19 11:52
     */
    protected function getFileData($type, $tableName, $moduleName)
    {
        $fileDir = $this->appPath . $moduleName . DS . $type . DS;
        $fileName = Str::studly($tableName);
        switch ($type) {
            case 'service':
                $fileName .= 'Service';
                break;
            case 'validate':
                $fileName .= 'Validate';
                break;
        }
        $filePath = $fileDir . $fileName . '.php';
        return [
            'dir' => $fileDir,
            'file_name' => $fileName,
            'file_path' => $filePath,
        ];
    }

}
